//
//  ProblemOffer20.swift
//  TestProject
//
//  Created by 武侠 on 2022/3/8.
//  Copyright © 2022 zhulong. All rights reserved.
//

import UIKit

/*
 剑指 Offer 20. 表示数值的字符串 ✅
 请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。

 数值（按顺序）可以分成以下几个部分：
 1: 若干空格
 2: 一个 小数 或者 整数
 3:（可选）一个 'e' 或 'E' ，后面跟着一个 整数
 4: 若干空格
 
 小数（按顺序）可以分成以下几个部分：
 1:（可选）一个符号字符（'+' 或 '-'）
 2: 下述格式之一：
    至少一位数字，后面跟着一个点 '.'
    至少一位数字，后面跟着一个点 '.' ，后面再跟着至少一位数字
    一个点 '.' ，后面跟着至少一位数字
 整数（按顺序）可以分成以下几个部分：
 1:（可选）一个符号字符（'+' 或 '-'）
 2: 至少一位数字
 
 部分数值列举如下：
 ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
 部分非数值列举如下：
 ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
 
 示例 1：输入：s = "0"    输出：true
 示例 2：输入：s = "e"    输出：false
 示例 3：输入：s = "."    输出：false
 示例 4：输入：s = "    .1  " 输出：true

 提示：
    1 <= s.length <= 20
    s 仅含英文字母（大写和小写），数字（0-9），加号 '+' ，减号 '-' ，空格 ' ' 或者点 '.' 。
 */
class ProblemOffer20: NSObject {
    /*
     所有的状态：
     0: 起始的空格
     1: 符号位 +/-
     2: 整数部分
     3: 整数后的点   （3.）
     4: 整数前的点   （2.3 或者 .4）
     5: 小数点后的整数部分
     6: 字符e
     7: 符号位 +/-
     8: 整数部分
     9: 末尾的空格
     
     " "以0开头：0: 0、1、2、4
     "s"以1开头：1: 2、4
     "d"以2开头：2: 2、3、6、9
     "."以3开头：3: 5、6、9
     "."以4开头：4: 5
     "d"以5开头：5: 5、6、9
     "e"以6开头：6: 7、8
     "s"以7开头：7: 8
     "d"以8开头：8: 8、9
     " "以9开头：9: 9
     
     能结尾的状态有：2、3、5、8、9
     */
    func isNumber(_ s: String) -> Bool {
        let states: [[Character : Int]] = [
            [" " : 0, "s" : 1, "d" : 2, "." : 4],
            ["d" : 2, "." : 4],
            ["d" : 2, "." : 3, "e" : 6, " " : 9],
            ["d" : 5, "e" : 6, " " : 9],
            ["d" : 5],
            ["d" : 5, "e" : 6, " " : 9],
            ["s" : 7, "d" : 8],
            ["d" : 8],
            ["d" : 8, " " : 9],
            [" " : 9]
        ]
        
        var p = 0
        for c in s {
            let sc = getNumber(c)
            if sc == "?" || states[p][sc] == nil {
                return false
            }
            p = states[p][sc]!
        }
        
        if p == 2 || p == 3 || p == 5 || p == 8 || p == 9 {
            return true
        }
        return false
    }
    
    func getNumber(_ c: Character) -> Character {
        switch c {
        case ("0"..."9"):
            return "d"
        case "+", "-":
            return "s"
        case "e", "E":
            return "e"
        case ".":
            return "."
        case " ":
            return " "
        default:
            return "?"
        }
    }
    
    
}
